package cn.bugstack.springframework.context.support;

import cn.bugstack.springframework.beans.BeansException;
import cn.bugstack.springframework.beans.factory.ConfigurableListableBeanFactory;
import cn.bugstack.springframework.beans.factory.config.BeanFactoryPostProcessor;
import cn.bugstack.springframework.beans.factory.config.BeanPostProcessor;
import cn.bugstack.springframework.context.ApplicationEvent;
import cn.bugstack.springframework.context.ApplicationListener;
import cn.bugstack.springframework.context.ConfigurableApplicationContext;
import cn.bugstack.springframework.context.event.ApplicationEventMulticaster;
import cn.bugstack.springframework.context.event.ContextClosedEvent;
import cn.bugstack.springframework.context.event.ContextRefreshedEvent;
import cn.bugstack.springframework.context.event.SimpleApplicationEventMulticaster;
import cn.bugstack.springframework.core.io.DefaultResourceLoader;

import java.util.Collection;
import java.util.Map;

/**
 * 默认情况下，Spring容器会使用 DefaultListableBeanFactory 类作为 BeanFactory 的实现，
 * 因此在大多数情况下，我们可以直接使用 ApplicationContext 的 getBeanFactory() 方法来获取默认的 DefaultListableBeanFactory 对象。
 * 但是，为了保持代码的灵活性和可扩展性，Spring使用 ConfigurableListableBeanFactory 接口来定义获取 BeanFactory 的方法，
 * 这样可以方便地切换不同的 BeanFactory 实现类，比如 DefaultListableBeanFactory、XmlBeanFactory 等。
 *
 * BeanPostProcessor 在 Spring 容器中的作用是对 Bean 对象进行初始化前后的处理。
 * 它允许在 Bean 实例化、依赖注入、初始化方法调用等过程中介入，对 Bean 进行定制化的处理操作。
 * BeanPostProcessor 需要在 Bean 对象实例化之前注册，是因为它需要在 Bean 实例化过程中介入，并在初始化之前进行处理。
 * 如果在 Bean 对象实例化之后才注册 BeanPostProcessor，那么已经实例化的 Bean 对象将无法被它所处理。
 * 因此，为了确保 BeanPostProcessor 能够在 Bean 实例化之前正确地对 Bean 进行处理，它需要在容器启动时提前注册好。
 * 通常情况下，Spring 容器会在初始化阶段注册所有的 BeanPostProcessor，并在适当的时机调用它们的相应方法，以确保对 Bean 的处理能够顺利进行。
 *
 */
// AbstractApplicationContext继承DefaultResourceLoader是为了处理spring.xml配置文件中配置资源的加载。
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {

    public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";

    private ApplicationEventMulticaster applicationEventMulticaster;

    // 支持刷新操作： 实现了应用上下文的刷新方法refresh()，该方法用于启动 Spring 应用上下文，完成所有的初始化操作，并准备好可用的 Bean 实例。
    @Override
    public void refresh() throws BeansException {
        // 1. 创建 BeanFactory，并加载 BeanDefinition
        refreshBeanFactory();

        // 2. 获取 BeanFactory
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();

        // 3. 添加 ApplicationContextAwareProcessor，让继承自 ApplicationContextAware 的 Bean 对象都能感知所属的 ApplicationContext
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

        // 4. 在 Bean 实例化之前，执行 BeanFactoryPostProcessor (Invoke factory processors registered as beans in the context.)
        invokeBeanFactoryPostProcessors(beanFactory);

        // 5. BeanPostProcessor 需要提前于其他 Bean 对象实例化之前执行注册操作
        registerBeanPostProcessors(beanFactory);

        // 6. 初始化事件发布者
        initApplicationEventMulticaster();

        // 7. 注册事件监听器
        registerListeners();

        // 8. 提前实例化单例Bean对象
        beanFactory.preInstantiateSingletons();

        // 9. 发布容器刷新完成事件
        finishRefresh();
    }

    // 将定义的抽象方法refreshBeanFactory和getBeanFactory由继承此抽象类的其他抽象类来实现。

    protected abstract void refreshBeanFactory() throws BeansException;

    protected abstract ConfigurableListableBeanFactory getBeanFactory();

    /**
     * 调用所有注册的BeanFactoryPostProcessor实例的postProcessBeanFactory方法。
     * 这个方法会遍历beanFactory中所有类型为BeanFactoryPostProcessor的bean，然后依次调用它们的
     * postProcessBeanFactory方法，以允许这些post-processors对bean factory进行修改。
     * beanFactory的getBeansOfType方法中会调用getBean方法，以获取对应的需要的Bean实例。
     * @param beanFactory 允许配置的可列表bean工厂，这是要被post-processors处理的bean工厂实例。
     */
    private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        // 获取所有类型为BeanFactoryPostProcessor的bean的映射。
        Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);

        // 遍历映射中的所有bean，调用它们的postProcessBeanFactory方法。
        for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactoryPostProcessorMap.values()) {
            beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
        }
    }


    /**
     * 注册Bean后处理器到BeanFactory中。
     * 这个方法会查询BeanFactory中所有类型的BeanPostProcessor实例，并将它们注册到BeanFactory中，
     * 以便在bean实例化后进行额外的处理。
     * beanFactory的addBeanPostProcessor方法的定义在ConfigurableBeanFactory接口中。
     * 该方法的实现是在AbstractBeanFactory抽象类中，会将实现BeanPostProcessor接口的Bean实例存放在下面列表中。
     * private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<BeanPostProcessor>();
     * @param beanFactory 允许配置的列表型bean工厂，用于从中获取和注册BeanPostProcessor实例。
     */
    private void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        // 从bean工厂中获取所有BeanPostProcessor类型的bean实例
        Map<String, BeanPostProcessor> beanPostProcessorMap = beanFactory.getBeansOfType(BeanPostProcessor.class);
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorMap.values()) {
            // 将每个获取到的BeanPostProcessor实例添加到bean工厂中
            beanFactory.addBeanPostProcessor(beanPostProcessor);
        }
    }


    private void initApplicationEventMulticaster() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, applicationEventMulticaster);
    }

    private void registerListeners() {
        Collection<ApplicationListener> applicationListeners = getBeansOfType(ApplicationListener.class).values();
        for (ApplicationListener listener : applicationListeners) {
            applicationEventMulticaster.addApplicationListener(listener);
        }
    }

    private void finishRefresh() {
        publishEvent(new ContextRefreshedEvent(this));
    }

    @Override
    public void publishEvent(ApplicationEvent event) {
        applicationEventMulticaster.multicastEvent(event);
    }

    /**
     * getBeansOfType方法的定义在ListableBeanFactory接口中
     * getBeansOfType方法的实现在DefaultListableBeanFactory类中
     */
    @Override
    public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
        return getBeanFactory().getBeansOfType(type);
    }

    @Override
    public String[] getBeanDefinitionNames() {
        return getBeanFactory().getBeanDefinitionNames();
    }

    @Override
    public Object getBean(String name) throws BeansException {
        return getBeanFactory().getBean(name);
    }

    @Override
    public Object getBean(String name, Object... args) throws BeansException {
        return getBeanFactory().getBean(name, args);
    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        return getBeanFactory().getBean(name, requiredType);
    }

    @Override
    public <T> T getBean(Class<T> requiredType) throws BeansException {
        return getBeanFactory().getBean(requiredType);
    }

    @Override
    public void registerShutdownHook() {
        Runtime.getRuntime().addShutdownHook(new Thread(this::close));
    }

    @Override
    public void close() {
        // 发布容器关闭事件
        publishEvent(new ContextClosedEvent(this));

        // 执行销毁单例bean的销毁方法
        getBeanFactory().destroySingletons();
    }
}
